/*
 * Copyright (c) 2012 the original author or authors.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
package org.libermundi.theorcs.security.tapestry.services.impl;

import java.io.UnsupportedEncodingException;
import java.security.InvalidKeyException;

import javax.servlet.http.Cookie;

import org.apache.tapestry5.services.RequestGlobals;
import org.libermundi.theorcs.core.model.Gender;
import org.libermundi.theorcs.core.tapestry.services.configuration.ApplicationConfig;
import org.libermundi.theorcs.security.SecurityConstants;
import org.libermundi.theorcs.security.exception.UserNotFoundException;
import org.libermundi.theorcs.security.model.User;
import org.libermundi.theorcs.security.services.SecurityManager;
import org.libermundi.theorcs.security.services.UserManager;
import org.libermundi.theorcs.security.tapestry.services.GigyaServices;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import com.gigya.socialize.GSDictionary;
import com.gigya.socialize.GSRequest;
import com.gigya.socialize.GSResponse;
import com.gigya.socialize.SigUtils;
import com.google.common.base.Strings;

public class GigyaServicesImpl implements GigyaServices {
	private static final Logger logger = LoggerFactory.getLogger(GigyaServicesImpl.class);
	
	private RequestGlobals _requestGlobals;
	
	private ApplicationConfig _appConfig;
	
	private SecurityManager _securityManager;
	
	private UserManager _userManager;
	
	public GigyaServicesImpl(RequestGlobals request, ApplicationConfig appConfig, SecurityManager securityManager, UserManager userManager) {
		_requestGlobals = request;
		_appConfig = appConfig;
		_securityManager = securityManager;
		_userManager = userManager;
	}

    @Override
	public boolean isSignatureValid(String gigyaUid, String timestamp, String signature) {
		try {
			if(SigUtils.validateUserSignature(gigyaUid, timestamp, _appConfig.getString(SecurityConstants.GIGYA_API_SECRET), signature)) {
				return Boolean.TRUE;
			}
		} catch (InvalidKeyException e) {
			logger.error("InvalidKeyException",e);
			return Boolean.FALSE;
		} catch (UnsupportedEncodingException e) {
			logger.error("UnsupportedEncodingException",e);
			return Boolean.FALSE;
		}

		logger.error("Gigya Signature is not valid !");
		return Boolean.FALSE;
	}

    @Override
	public void authenticate(String gigyaUid, boolean rememberMe) throws UserNotFoundException {
		User user = _userManager.getUserByUID(gigyaUid,Boolean.TRUE);
		if(user != null) {
			_securityManager.authenticate(user,rememberMe,_requestGlobals.getHTTPServletRequest(),_requestGlobals.getHTTPServletResponse());
			notifyLogin(user);
			logger.debug("User found in the database. Authenticated user : " + user.getUsername());
		} else {
			logger.debug("No matching user in the database");
			throw new UserNotFoundException("No matching user in the database");
		}
	}

	public void notifyLogin(User user) {
		logger.debug("Notifying Login of '{}' to Gigya",user.getUsername());
		
		GSDictionary userInfo = new GSDictionary();
		
		userInfo.put("nickname", user.getNickName());
		if(!Strings.isNullOrEmpty(user.getAvatar())){
			userInfo.put("photoURL", user.getAvatar());
		}
		
		if(!Strings.isNullOrEmpty(user.getThumbnail())){
			userInfo.put("thumbnailURL", user.getThumbnail());
		}
		
		if(!Strings.isNullOrEmpty(user.getFirstName())){
			userInfo.put("firstName", user.getFirstName());
		}
		
		if(!Strings.isNullOrEmpty(user.getLastName())){
			userInfo.put("lastName", user.getLastName());
		}
	
		if(user.getGender().equals(Gender.MALE)) {
			userInfo.put("gender", "m");
		} else if (user.getGender().equals(Gender.FEMALE)) {
			userInfo.put("gender", "n");
		}
		
		// TODO : Implement Age feature
		// userInfo.put("age", user.getAge());
		
		userInfo.put("email", user.getEmail());
		
		GSRequest gsreq = getRequest(GIGYA_NOTIFY_LOGIN); 
		gsreq.setParam("siteUID", user.getUid());
		gsreq.setParam("cid","Login Form");
		gsreq.setParam("userInfo", userInfo);
		GSResponse gsresp = gsreq.send();
		
		//Now we need to set a Cookie on client Browser
		if(gsresp.getErrorCode()==0) {
			Cookie gigyaCookie = new Cookie(gsresp.getString("cookieName",""), gsresp.getString("cookieValue",""));
			gigyaCookie.setPath(gsresp.getString("cookiePath","/"));
			gigyaCookie.setDomain(gsresp.getString("cookieDomain","http://localhost"));
			_requestGlobals.getHTTPServletResponse().addCookie(gigyaCookie);
		} else {
			logger.error("Error when notifying Login to Gigya");
			logger.error(gsresp.getErrorMessage());
		}
	}

    @Override
	public boolean notifyRegistration(String uid, User user, String contextId) {
		GSRequest gsreq = getRequest(GIGYA_NOTIFY_REGISTRATION); 
		gsreq.setParam("uid", uid);
		gsreq.setParam("siteUID", user.getUid());
		gsreq.setParam("cid",contextId);
		GSResponse gsresp = gsreq.send();
		if(gsresp.getErrorCode()!=0) {
			logger.error("Error when notifying Registration to Gigya");
			logger.error(gsresp.getErrorMessage() + " : " + gsresp.getErrorCode());
			
			// TODO : CHANGE THAT !!!!!
			// Maybe the Gigya account is already bound to a deleted account ?
			
			
			if(gsresp.getErrorCode() == 409001) {
				user.setUid(uid); // Weird case where the UID does not match our User DB but UID is already set in Gigya
				return Boolean.TRUE;
			}
			return Boolean.FALSE;
		}
		return Boolean.TRUE;
	}
	
    @Override
	public boolean setUid(String uid, User user) {
		GSRequest gsreq = getRequest(GIGYA_SETUID); 
		gsreq.setParam("uid", uid);
		gsreq.setParam("siteUID", user.getUid());
		GSResponse gsresp = gsreq.send();
		if(gsresp.getErrorCode()!=0) {
			logger.error("Error when setting Gigya UID {} to User {}",user.getUid(),user.getUsername());
			logger.error(gsresp.getErrorMessage());
			return Boolean.FALSE;
		}
		logger.debug("Successfully assigned Gigya UID {} to User {}",user.getUid(),user.getUsername());
		return Boolean.TRUE;
	}
	
    @Override
	public boolean deleteAccount(String uid) {
		GSRequest gsreq = getRequest(GIGYA_DELETE_ACCOUNT); 
		gsreq.setParam("uid", uid);
		GSResponse gsresp = gsreq.send();
		if(gsresp.getErrorCode()!=0) {
			logger.error("Error when deleting Gigya Account {}",uid);
			logger.error(gsresp.getErrorMessage());
			return Boolean.FALSE;
		}
		if(logger.isDebugEnabled()) {
			logger.debug("Successfully deleted Gigya Account {}",uid);
		}
		return Boolean.TRUE;
	}

	private GSRequest getRequest(String apiMethod){
		GSRequest request =  new GSRequest(_appConfig.getString(SecurityConstants.GIGYA_API_KEY), _appConfig.getString(SecurityConstants.GIGYA_API_SECRET), apiMethod);
		return request;		
	}

}
